// Copyright 2014 Flávio Ribeiro <flavio@bem.tv>.
// All rights reserved.
// Use of this source code is governed by Apache
// license that can be found in the LICENSE file.

var BaseObject = require('base_object')
var Settings = require('./settings')
var log = require('./log').getInstance()

class CDNRequester extends BaseObject {
  get name() { return 'CDNRequester' }
  constructor() {
    this.utils = new Worker(this.getWorkerURL())
    this.utils.onmessage = (e) => this.response(e)
  }

  getWorkerURL() {
    // non-minified version at https://gist.github.com/flavioribeiro/d706afbf83d055439f21
    /*worker lắng nghe sự kiện message và gán e = url, r = allowCredentials. Sau đó gọi request funtion*/
    //var content = 'request=function(e,r,t){var n=new XMLHttpRequest;r&&(n.withCredentials=!0),n.open("GET",e,t?!0:!1),n.responseType="arraybuffer",n.onload=function(e){console.log(200);t(200===n.status?e.currentTarget.response:"")},n.send()},base64ArrayBuffer=function(e){for(var r,t,n,s,a,o="",u="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",i=new Uint8Array(e),d=i.byteLength,f=d%3,c=d-f,p=0;c>p;p+=3)a=i[p]<<16|i[p+1]<<8|i[p+2],r=(16515072&a)>>18,t=(258048&a)>>12,n=(4032&a)>>6,s=63&a,o+=u[r]+u[t]+u[n]+u[s];return 1==f?(a=i[c],r=(252&a)>>2,t=(3&a)<<4,o+=u[r]+u[t]+"=="):2==f&&(a=i[c]<<8|i[c+1],r=(64512&a)>>10,t=(1008&a)>>4,n=(15&a)<<2,o+=u[r]+u[t]+u[n]+"="),o},resourceLoaded=function(e){var e=base64ArrayBuffer(e);this.postMessage(e)},this.addEventListener("message",function(e){var r=JSON.parse(e.data);request(r.url,r.allowCredentials,resourceLoaded.bind(this))},!1);'
    var content = 'request = function(e, r, t, o) { var n = new XMLHttpRequest; r && (n.withCredentials = !0), n.open("GET", e, t ? !0 : !1), n.responseType = "arraybuffer", n.onload = function(e) {t(200 === n.status ? e.currentTarget.response : "") }, n.onerror = function(e) { o(e) }, n.send() }, base64ArrayBuffer = function(e) { for (var r, t, n, s, a, o = "", u = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", i = new Uint8Array(e), d = i.byteLength, f = d % 3, c = d - f, p = 0; c > p; p += 3) a = i[p] << 16 | i[p + 1] << 8 | i[p + 2], r = (16515072 & a) >> 18, t = (258048 & a) >> 12, n = (4032 & a) >> 6, s = 63 & a, o += u[r] + u[t] + u[n] + u[s]; return 1 == f ? (a = i[c], r = (252 & a) >> 2, t = (3 & a) << 4, o += u[r] + u[t] + "==") : 2 == f && (a = i[c] << 8 | i[c + 1], r = (64512 & a) >> 10, t = (1008 & a) >> 4, n = (15 & a) << 2, o += u[r] + u[t] + u[n] + "="), o }, resourceLoaded = function(e) { var e = base64ArrayBuffer(e); var j = { type: "data", data: e }; sendToMaster(j) }, resourceLoadError = function(e) { var e ={type: "error"}; sendToMaster(e) }, sendToMaster = function(e) { this.postMessage(e) }, this.addEventListener("message", function(e) { var r = JSON.parse(e.data); request(r.url, r.allowCredentials, resourceLoaded.bind(this), resourceLoadError.bind(this)) }, !1);'

    var blob
    try {
      blob = new Blob([content], {type: 'application/javascript'})
    } catch (e) {
      window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder
      blob = new BlobBuilder()
      blob.append(content)
      blob = blob.getBlob()
    }

    /*Trả về blob URL tham chiếu tới file worker 
    (vì thông thường khi tạo 1 worker thì: myWorker = new Worker(path/file.js)) 
    Ở đây chuyển đoạn string trong content thành một blob và tạo một blob url tham chiếu tới blob này*/
    return URL.createObjectURL(blob) 
  }

  requestResource(resource, callback, requestError) {
    // postMessage worker chứa thông tin url, allowCredentials
    this.callback = callback
    this.resource = resource
    this.requestError = requestError
    var message = {'url':resource, 'allowCredentials': Settings.forceAllowCredentials}
    this.utils.postMessage(JSON.stringify(message))  
    this.startRequest = Date.now()
  }

  response(e) {
    if (e.data.type === "error") {
      this.requestError()
    } else {
      this.resourceLoaded(e.data.data)
    }
  }

  resourceLoaded(chunk) {
    // request thành công và trả về data
    if (chunk.length === 0) {
      // Không có data => request lại
      log.warn("error fetching from CDN, retrying")
      this.requestResource(this.resource, this.callback, this.requestError)
    } else {
      // Có data gọi hàm callback
      var downloadTime = Date.now() - this.startRequest
      this.trigger('cdnrequester:downloadtime', {downloadTime: downloadTime, type: 'cdn'})
      this.callback(chunk, "cdn")
    }
  }
}

module.exports = CDNRequester
